package it.bananagiallo.scarycamera;

import android.app.Activity;
import android.app.AlertDialog;
import android.hardware.Camera;
import android.media.AudioManager;
import android.media.SoundPool;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Vibrator;
import android.widget.FrameLayout;
import android.widget.ImageView;

import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.hardware.Camera.AutoFocusCallback;
import android.hardware.Camera.CameraInfo;
import android.hardware.Camera.Size;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;

import java.io.IOException;
import java.util.List;

// ----------------------------------------------------------------------

public class CameraPreview extends Activity {
    private Preview mPreview;
    Camera mCamera;
    int numberOfCameras;
    int cameraCurrentlyLocked;
    SoundPool mSoundPool;
    int camerafocusSound;
    int cameragotfocusSound;
    int scarySound;
    
    Vibrator mVibrator;
    
    Handler mScreamerHandler;
    ScreamerThread mScreamerThread;
    
    ImageView mImageViewScary;

    // The first rear facing camera
    int defaultCameraId;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        // Hide the window title.
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
        
        // Load the layout from xml
        setContentView(R.layout.camera);

        // Create a RelativeLayout container that will hold a SurfaceView,
        // and set it as the content of our activity.
        mPreview = new Preview(this);
        FrameLayout mCameraFrame = (FrameLayout) findViewById(R.id.cameraFrame);
        mCameraFrame.addView(mPreview);

        // Find the total number of cameras available
        numberOfCameras = Camera.getNumberOfCameras();

        // Find the ID of the default camera
        CameraInfo cameraInfo = new CameraInfo();
        for (int i = 0; i < numberOfCameras; i++) {
            Camera.getCameraInfo(i, cameraInfo);
            if (cameraInfo.facing == CameraInfo.CAMERA_FACING_BACK) {
                defaultCameraId = i;
            }
        }
            
        // Set the event onClick of ShootButton
        ImageView mButtonShoot = (ImageView) findViewById(R.id.buttonShoot);
        mButtonShoot.setOnClickListener(new OnClickListener() {
        	
			@Override
			public void onClick(View v) {
				
				//Get a fake shoot
				startShoot();
				
			}
		});
        
        // Set the SoundPool for focus and scary sound
        mSoundPool = new SoundPool(1, AudioManager.STREAM_NOTIFICATION, 0);
        camerafocusSound = mSoundPool.load(this, R.raw.camerafocus, 0);
        cameragotfocusSound = mSoundPool.load(this, R.raw.cameragotfocus, 0);
        scarySound = mSoundPool.load(this, R.raw.scream, 0);
        
        // Bind Scary Image
        mImageViewScary = (ImageView) findViewById(R.id.imageScary);
        mImageViewScary.setAlpha(0);
        
        // Bind vibrator
        mVibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
        
        // Set runnable to execute the scream
        mScreamerHandler = new ScreamerHandler();
        mScreamerThread = new ScreamerThread(mScreamerHandler);
        
    }

    @Override
    protected void onResume() {
        super.onResume();

        // Open the default i.e. the first rear facing camera.
        mCamera = Camera.open();
        cameraCurrentlyLocked = defaultCameraId;
        mPreview.setCamera(mCamera);
    }

    @Override
    protected void onPause() {
        super.onPause();

        // Because the Camera object is a shared resource, it's very
        // important to release it when the activity is paused.
        if (mCamera != null) {
            mPreview.setCamera(null);
            mCamera.release();
            mCamera = null;
        }
    }

    private void startShoot()
    {
    	
    	//Autofocus sound
    	final int camerafocusSoundInQueue = mSoundPool.play(camerafocusSound, 1f, 1f, 0, 0, 1);
    	
    	// Execute autofocus
    	mCamera.autoFocus(new AutoFocusCallback() {

			@Override
			public void onAutoFocus(boolean success, Camera camera) {
				
				// Stop autofocus sound
				mSoundPool.stop(camerafocusSoundInQueue);
				
				// Autofocus got sound
		    	mSoundPool.play(cameragotfocusSound, 1f, 1f, 0, 0, 1);
		    	
		    	// Let's Scream!!!
		    	new Handler().postDelayed(mScreamerThread, 500);
		    	
			}
		});
    }
    
    private void startScream()
    {
    	
    	// Play scary sound
    	mSoundPool.play(scarySound, 1f, 1f, 0, 0, 1);
    	
    	// Show scary image
    	mImageViewScary.setAlpha(255);
    	
    	// Vibrate
    	mVibrator.vibrate(2000);
    	
    }
    
	private class ScreamerHandler extends Handler {
	    	
	        @Override
	        public void handleMessage(Message msg) {
	        	
	        	try {
	        		
	        		startScream();
	        	
	    		} catch (Exception e) {}
	    	}
	    }
	
	private class ScreamerThread extends Thread {
    	
    	private Handler mHandler;
    	
    	public ScreamerThread(Handler handler)
    	{
    		
    		this.mHandler = handler;
    		
    	}

		@Override
		public void run() {
			
			//Send message to start scream later
			Message msg = mHandler.obtainMessage();
			mHandler.sendMessage(msg);
			
		}
    	
    }
    
}

// ----------------------------------------------------------------------

/**
 * A simple wrapper around a Camera and a SurfaceView that renders a centered preview of the Camera
 * to the surface. We need to center the SurfaceView because not all devices have cameras that
 * support preview sizes at the same aspect ratio as the device's display.
 */
class Preview extends ViewGroup implements SurfaceHolder.Callback {
    private final String TAG = "Preview";

    SurfaceView mSurfaceView;
    SurfaceHolder mHolder;
    Size mPreviewSize;
    List<Size> mSupportedPreviewSizes;
    Camera mCamera;

    Preview(Context context) {
        super(context);

        mSurfaceView = new SurfaceView(context);
        addView(mSurfaceView);

        // Install a SurfaceHolder.Callback so we get notified when the
        // underlying surface is created and destroyed.
        mHolder = mSurfaceView.getHolder();
        mHolder.addCallback(this);
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    public void setCamera(Camera camera) {
        mCamera = camera;
        if (mCamera != null) {
            mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
            
            //Setting flash options
            Camera.Parameters parameters = camera.getParameters();
            parameters.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO);
            camera.setParameters(parameters);
            
            requestLayout();
        }
    }

    public void switchCamera(Camera camera) {
       setCamera(camera);
       try {
           camera.setPreviewDisplay(mHolder);
       } catch (IOException exception) {
           Log.e(TAG, "IOException caused by setPreviewDisplay()", exception);
       }
       Camera.Parameters parameters = camera.getParameters();
       parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
       requestLayout();

       camera.setParameters(parameters);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // We purposely disregard child measurements because act as a
        // wrapper to a SurfaceView that centers the camera preview instead
        // of stretching it.
        final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
        final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);
        setMeasuredDimension(width, height);

        if (mSupportedPreviewSizes != null) {
            mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height);
        }
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if (changed && getChildCount() > 0) {
            final View child = getChildAt(0);

            final int width = r - l;
            final int height = b - t;

            int previewWidth = width;
            int previewHeight = height;
            if (mPreviewSize != null) {
                previewWidth = mPreviewSize.width;
                previewHeight = mPreviewSize.height;
            }

            // Center the child SurfaceView within the parent.
            if (width * previewHeight > height * previewWidth) {
                final int scaledChildWidth = previewWidth * height / previewHeight;
                child.layout((width - scaledChildWidth) / 2, 0,
                        (width + scaledChildWidth) / 2, height);
            } else {
                final int scaledChildHeight = previewHeight * width / previewWidth;
                child.layout(0, (height - scaledChildHeight) / 2,
                        width, (height + scaledChildHeight) / 2);
            }
        }
    }

    public void surfaceCreated(SurfaceHolder holder) {
        // The Surface has been created, acquire the camera and tell it where
        // to draw.
        try {
            if (mCamera != null) {
                mCamera.setPreviewDisplay(holder);
            }
        } catch (IOException exception) {
            Log.e(TAG, "IOException caused by setPreviewDisplay()", exception);
        }
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        // Surface will be destroyed when we return, so stop the preview.
        if (mCamera != null) {
            mCamera.stopPreview();
        }
    }

    private Size getOptimalPreviewSize(List<Size> sizes, int w, int h) {
        final double ASPECT_TOLERANCE = 0.1;
        double targetRatio = (double) w / h;
        if (sizes == null) return null;

        Size optimalSize = null;
        double minDiff = Double.MAX_VALUE;

        int targetHeight = h;

        // Try to find an size match aspect ratio and size
        for (Size size : sizes) {
            double ratio = (double) size.width / size.height;
            if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
            if (Math.abs(size.height - targetHeight) < minDiff) {
                optimalSize = size;
                minDiff = Math.abs(size.height - targetHeight);
            }
        }

        // Cannot find the one match the aspect ratio, ignore the requirement
        if (optimalSize == null) {
            minDiff = Double.MAX_VALUE;
            for (Size size : sizes) {
                if (Math.abs(size.height - targetHeight) < minDiff) {
                    optimalSize = size;
                    minDiff = Math.abs(size.height - targetHeight);
                }
            }
        }
        return optimalSize;
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        // Now that the size is known, set up the camera parameters and begin
        // the preview.
    	if(mCamera != null)
    	{
	        Camera.Parameters parameters = mCamera.getParameters();
	        parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
	        requestLayout();
	
	        mCamera.setParameters(parameters);
	        mCamera.startPreview();
    	}
    }
    
}